PCB image added
[LED tetris] / tetris.c
1 /*
2  * TETRIS for AVR microcontrollers and cascading LED matrix based on MAX7219 driver
3  * Created for Interplaymediumâ„¢ project (https://interplaymedium.org)
4  * Copyright Â© 2010 Dmitry Shalnov [interplaymedium.org]
5  * Licensed under the Apache License, Version 2.0
6 */
7
8 #include <avr/io.h>
9 #include <avr/interrupt.h>
10
11 // WIRING 
12 // please change it according to your ARDUINO wiring
13 // don't forget to place pull-up resisor between RESET and +5V pins to avoid board reset during UART transfer.
14
15 #define DATAIN  3       
16 #define LOAD    5       
17 #define CLOCK   4       
18 #define BEEPER  2       
19
20 #define INUSE   1       // how many matrix you connect
21 #define STARTX  4       // horisontal position od figure strat
22 #define RAND_MAX   4
23
24 // Some macros that make the code more readable
25
26 #define output_low(port,pin) port &= ~(1<<pin)
27 #define output_high(port,pin) port |= (1<<pin)
28 #define set_input(portdir,pin) portdir &= ~(1<<pin)
29 #define set_output(portdir,pin) portdir |= (1<<pin)
30
31 #define FOSC    16000000
32 #define BAUD    9600
33 #define UBRR    FOSC/16/BAUD-1
34
35 volatile unsigned char u, oldu;
36
37 // ------------------------------------------- Set Receive Interrupt Enable ------------------------------------------- 
38
39 void setRXCIE_USART0()
40 {
41     UCSR0B |= _BV(RXCIE0);
42 }
43
44 // -------------------------------------------- Initialize USART0 -----------------------------------------------------
45
46 void init_USART0 (unsigned int baud) {
47         UBRR0 = baud;                           // Set Baudrate
48         UCSR0C = (3<<UCSZ00);                   // Character Size 8 bit
49         UCSR0B |= _BV(RXEN0) | _BV(TXEN0);      // Receiver and Transmitter Enable
50 }
51
52 // -------------------------------------------- Receive 1 byte  Data --------------------------------------------------
53
54 unsigned char receive_1byte_USART0 (void) {
55         loop_until_bit_is_set(UCSR0A, RXC0);
56         return UDR0;
57 }
58
59 // -------------------------------------------- Transmit 1 byte Data --------------------------------------------------
60
61 void transmit_1byte_USART0 (unsigned char data) {
62         loop_until_bit_is_set(UCSR0A, UDRE0);
63         UDR0 = data;
64 }
65
66 // -------------------------------------------- Interrupt handler -----------------------------------------------------
67
68 ISR(USART_RX_vect){ // USART RX interrupt
69         volatile unsigned char c;
70         c = UDR0;
71         //transmit_1byte_USART0('w');
72         //output_high(PORTB, LED1);
73         //if (c<'5' && c>'0') 
74         u = c;
75 }
76
77 // --------------------------------------------- Transmit String Data -------------------------------------------------
78
79 void transmit_str_USART0 (char *str) {
80         while (*str != 0) {
81                 transmit_1byte_USART0(*str);
82                 *str++;
83         }
84 }
85
86 // --------------------------------------------- Transmit Four-Digit Integer ------------------------------------------
87
88 void transmit_4digit_USART0 (int num) {
89         unsigned char temp;
90         int digit = 1000;
91
92         while (digit != 0) {
93                 temp = num / digit;
94                 transmit_1byte_USART0('0'+temp);
95                 num -= (digit*temp);
96                 digit /= 10;
97         }
98 }
99
100 // --------------------------------------------- Delays --------------------------------------------------------------
101
102 void delay_ms (uint16_t ms) {
103         uint16_t delay_count = FOSC / 17500 * 0.1;
104         volatile uint16_t i;
105         while (ms != 0) {
106                 for (i=0; i != delay_count; i++);
107                 ms--;
108         }
109 }
110
111 void delay_ns(uint16_t ms) {
112         uint16_t delay_count = FOSC / 17500 * 0.01;
113         volatile uint16_t i;
114         while (ms != 0) {
115                 for (i=0; i != delay_count; i++);
116                 ms--;
117         }
118 }
119
120 // --------------------------------------------- Sounds -------------------------------------------------------------
121
122 void beep (){
123         for(uint8_t a=0; a<100; a++){
124                 output_high(PORTB, BEEPER);
125                 delay_ms(1);
126                 output_low(PORTB, BEEPER);
127                 delay_ms(1);
128         }
129 }
130
131 void bleepBleepSound (){
132         uint8_t a, b;
133         for(b=1; b<40; b+=10) for(a=0; a<100; a++){
134                 output_high(PORTB, BEEPER);
135                 delay_ns(b);
136                 output_low(PORTB, BEEPER);
137                 delay_ns(b);
138         }
139 }
140
141 // --------------------------------- LED MATRIX code ---------------------------------------------------------------
142
143 // define max7219 registers
144 uint8_t max7219_reg_noop        = 0x00;
145 uint8_t max7219_reg_digit0      = 0x01;
146 uint8_t max7219_reg_digit1      = 0x02;
147 uint8_t max7219_reg_digit2      = 0x03;
148 uint8_t max7219_reg_digit3      = 0x04;
149 uint8_t max7219_reg_digit4      = 0x05;
150 uint8_t max7219_reg_digit5      = 0x06;
151 uint8_t max7219_reg_digit6      = 0x07;
152 uint8_t max7219_reg_digit7      = 0x08;
153 uint8_t max7219_reg_decodeMode  = 0x09;
154 uint8_t max7219_reg_intensity   = 0x0a;
155 uint8_t max7219_reg_scanLimit   = 0x0b;
156 uint8_t max7219_reg_shutdown    = 0x0c;
157 uint8_t max7219_reg_displayTest = 0x0f;
158
159
160 void putByte(uint8_t data) {
161         uint8_t i = 8;
162         uint8_t mask;
163         while(i > 0) {
164                 mask = 0x01 << (i - 1);      // get bitmask
165                 //digitalWrite( CLOCK, LOW);   // tick
166                 output_low(PORTB, CLOCK);       // tick
167                 if (data & mask){            // choose bit
168                         //digitalWrite(DATAIN, HIGH);// send 1
169                         output_high(PORTB, DATAIN);
170                 } else {
171                      //digitalWrite(DATAIN, LOW); // send 0
172                         output_low(PORTB, DATAIN);
173                 }
174                 //digitalWrite(CLOCK, HIGH);   // tock
175                 output_high(PORTB, CLOCK);
176                 --i;                         // move to lesser bit
177         }
178 }
179
180 void maxOne(uint8_t maxNr, uint8_t reg, uint8_t col) {    
181         //maxOne is for adressing different MAX7219's, 
182         //whilele having a couple of them cascaded
183         int c = 0;
184         //digitalWrite(load, LOW);  // begin     
185         output_low(PORTB, LOAD);
186
187         for ( c = INUSE; c > maxNr; c--) {
188                 putByte(0);    // means no operation
189                 putByte(0);    // means no operation
190         }
191
192         putByte(reg);  // specify register
193         putByte(col);//((data & 0x01) * 256) + data >> 1); // put data 
194
195         for ( c = maxNr-1; c >= 1; c--) {
196                 putByte(0);    // means no operation
197                 putByte(0);    // means no operation
198         }
199
200         //digitalWrite(load, LOW); // and load da shit
201         output_low(PORTB, LOAD);
202         //digitalWrite(load,HIGH); 
203         output_high(PORTB, LOAD);
204 }
205
206 /*
207 void putPixel (uint8_t maxNr, uint8_t x, uint8_t y){
208         //maxOne is for adressing different MAX7219's, 
209         //whilele having a couple of them cascaded
210
211         int c = 0;
212         //digitalWrite(load, LOW);  // begin     
213         output_low(PORTB, LOAD);
214
215         for ( c = INUSE; c > maxNr; c--) {
216                 putByte(0);    // means no operation
217                 putByte(0);    // means no operation
218         }
219
220         putByte(y);  // specify register
221         putByte(0x01 << (8-x));//((data & 0x01) * 256) + data >> 1); // put data 
222
223         for ( c = maxNr-1; c >= 1; c--) {
224                 putByte(0);    // means no operation
225                 putByte(0);    // means no operation
226         }
227
228         //digitalWrite(load, LOW); // and load da shit
229         output_low(PORTB, LOAD);
230         //digitalWrite(load,HIGH); 
231         output_high(PORTB, LOAD);
232 }
233 */
234
235 // --------------------------------- Figures ----------------------------------------------------------------------
236
237 uint8_t figure[5][4][4] = {     
238                                 {
239                                         {
240                                         0b00011000,
241                                         0b00011000,
242                                         0b00000000,
243                                         0b00000000
244                                         },
245                                         {
246                                         0b00011000,
247                                         0b00011000,
248                                         0b00000000,
249                                         0b00000000
250                                         },
251                                         {
252                                         0b00011000,
253                                         0b00011000,
254                                         0b00000000,
255                                         0b00000000
256                                         },
257                                         {
258                                         0b00011000,
259                                         0b00011000,
260                                         0b00000000,
261                                         0b00000000
262                                         }
263                                 },  
264                                 {
265                                         {
266                                         0b00110000,
267                                         0b00011000,
268                                         0b00000000,
269                                         0b00000000
270                                         },
271                                         {
272                                         0b00010000,
273                                         0b00110000,
274                                         0b00100000,
275                                         0b00000000
276                                         },
277                                         {
278                                         0b00110000,
279                                         0b00011000,
280                                         0b00000000,
281                                         0b00000000
282                                         },
283                                         {
284                                         0b00010000,
285                                         0b00110000,
286                                         0b00100000,
287                                         0b00000000
288                                         }
289                                 }, 
290                                 {
291                                         {
292                                         0b00000000,
293                                         0b00111000,
294                                         0b00001000,
295                                         0b00000000
296                                         },
297                                         {
298                                         0b00011000,
299                                         0b00010000,
300                                         0b00010000,
301                                         0b00000000
302                                         },
303                                         {
304                                         0b00100000,
305                                         0b00111000,
306                                         0b00000000,
307                                         0b00000000
308                                         },
309                                         {
310                                         0b00010000,
311                                         0b00010000,
312                                         0b00110000,
313                                         0b00000000
314                                         }
315                                 }, 
316                                 {
317                                         {
318                                         0b00010000,
319                                         0b00111000,
320                                         0b00000000,
321                                         0b00000000
322                                         },
323                                         {
324                                         0b00010000,
325                                         0b00110000,
326                                         0b00010000,
327                                         0b00000000
328                                         },
329                                         {
330                                         0b00000000,
331                                         0b00111000,
332                                         0b00010000,
333                                         0b00000000
334                                         },
335                                         {
336                                         0b00010000,
337                                         0b00011000,
338                                         0b00010000,
339                                         0b00000000
340                                         }
341                                 }, 
342                                 {
343                                         {
344                                         0b00000000,
345                                         0b00111100,
346                                         0b00000000,
347                                         0b00000000
348                                         },
349                                         {
350                                         0b00010000,
351                                         0b00010000,
352                                         0b00010000,
353                                         0b00010000
354                                         },
355                                         {
356                                         0b00000000,
357                                         0b00111100,
358                                         0b00000000,
359                                         0b00000000
360                                         },
361                                         {
362                                         0b00010000,
363                                         0b00010000,
364                                         0b00010000,
365                                         0b00010000
366                                         }
367                                 }
368                           };
369
370  
371 // ----------------------------------------- Init variables ------------------------------------------------------------
372
373 uint8_t screen[ INUSE*8 + 1 ];
374
375 uint8_t currentFigure = 4;
376 uint8_t currentTurn = 0;
377 uint8_t currentY = 0;
378 uint8_t currentX = STARTX;
379 uint32_t timer = 0;
380 uint8_t randomDigit = 0;
381 uint8_t prewRandomDigit;
382 uint8_t score = 1; 
383
384 // ------------------------------------------ Game logic ---------------------------------------------------------------
385
386 uint8_t moveLine(uint8_t L, uint8_t X){
387         if (X <= 4 ) L <<= 4 - X; else L >>= X - 4;
388         return L;
389 }
390
391 void redrawScreen (void) {
392         uint8_t lineToPlace = 0b00000000;
393         for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
394                 if (matrix*8 + line >= currentY && matrix*8 + line < currentY+4) lineToPlace = figure[currentFigure][currentTurn][matrix*8 +line - currentY]; else lineToPlace = 0b00000000;
395                 //lineToPlace = 0b11111111; else lineToPlace = 0b00000000;
396                 //if (currentX <= 4 ) lineToPlace <<= 4 - currentX; else lineToPlace >>= currentX - 4;
397                 lineToPlace = moveLine(lineToPlace, currentX);
398                 maxOne(matrix+1, line+1, screen[matrix*8 + line] | lineToPlace );       
399         }
400 }
401
402 uint8_t checkDown (void){
403         uint8_t lineToPlace = 0b00000000;
404         for (uint8_t line = 0; line<4; line ++){
405                 lineToPlace = figure[currentFigure][currentTurn][line];
406                 lineToPlace = moveLine(lineToPlace, currentX);
407                 if ( (lineToPlace & screen[ currentY+1 + line]) != 0) return 1;
408         }
409         return 0;
410 }
411
412 uint8_t checkStart (void){
413         uint8_t lineToPlace = 0b00000000;
414         for (uint8_t line = 0; line<4; line ++){
415                 lineToPlace = figure[currentFigure][currentTurn][line];
416                 lineToPlace = moveLine(lineToPlace, currentX);
417                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
418         }
419         return 0;
420 }
421
422 uint8_t checkLeft (void){
423         uint8_t lineToPlace = 0b00000000;
424         uint8_t tmpLineToPlace;
425         for (uint8_t line = 0; line<4; line ++){
426                 lineToPlace = figure[currentFigure][currentTurn][line];
427                 tmpLineToPlace = moveLine(lineToPlace, currentX);
428                 if ( (tmpLineToPlace & 0b10000000) != 0) return 1;
429                 lineToPlace = moveLine(lineToPlace, currentX - 1);
430                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
431         }
432         return 0;
433 }
434
435 uint8_t checkRight (void){
436         uint8_t lineToPlace = 0b00000000;
437         uint8_t tmpLineToPlace;
438         for (uint8_t line = 0; line<4; line ++){
439                 lineToPlace = figure[currentFigure][currentTurn][line];         
440                 tmpLineToPlace = moveLine(lineToPlace, currentX);
441                 if ( (tmpLineToPlace & 0b00000001) != 0) return 1;
442                 lineToPlace = moveLine(lineToPlace, currentX + 1);
443                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
444         }
445         return 0;
446 }
447
448 uint8_t checkTurn (void){
449         uint8_t lineToPlace = 0b00000000;
450         uint8_t testTurn; 
451         if (currentTurn < 3) testTurn =  currentTurn + 1; else testTurn = 0;
452         for (uint8_t line = 0; line<4; line ++){
453                 lineToPlace = figure[currentFigure][testTurn][line];
454                 lineToPlace = moveLine(lineToPlace, currentX);
455                 //if ( (lineToPlace & 0b00000001) != 0) return 1; 
456                 if (currentFigure == 1 && currentX == 8) return 1;
457                 if (currentFigure == 2 && currentX == 1) return 1;
458                 if (currentFigure == 2 && currentX == 8) return 1;
459                 if (currentFigure == 3 && currentX == 1) return 1;
460                 if (currentFigure == 3 && currentX == 8) return 1;   
461                 if (currentFigure == 4 && currentX == 2) return 1;
462                 if (currentFigure == 4 && currentX == 7) return 1; 
463                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
464         }
465         return 0;
466 }
467
468 void checkFullLine (void) {
469         uint8_t a = 0;
470         uint8_t matrix = currentY / (INUSE*8);
471         uint8_t remainder = currentY % (INUSE*8);
472         for (uint8_t line = 0; line<4; line ++) if (line + currentY < INUSE*8) {
473                 if (screen[line + currentY] == 0b11111111) {
474                         for (a = 0; a<3; a++) {
475                                 
476                                 maxOne(matrix + 1, line + remainder +1, 0b00000000 );
477                                 //screen[line + currentY] = 0b11111111; 
478                                 //redrawScreen ();
479                                 delay_ms(1000);
480                                 maxOne(matrix + 1, line + remainder +1, 0b11111111 );   
481                                 //screen[line + currentY] = 0b00000000;
482                                 //redrawScreen ();
483                                 delay_ms(1000);
484                         }
485                         for (a = line + currentY; a>0; a--) screen[a] = screen[a-1];
486                         bleepBleepSound ();
487                         transmit_str_USART0("Score: "); 
488                         transmit_4digit_USART0(score++);
489                         transmit_str_USART0("        \r");
490                 }
491         }
492 }
493
494 void gameOver () {
495         uint8_t ornament[3] = {0b00100100, 0b10010010, 0b01001001};
496         uint8_t offset = 0;
497         for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
498                 maxOne(matrix + 1, line + 1, 0b11111111 );
499                 delay_ms(500);
500         }
501         for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
502                 maxOne(matrix + 1, line + 1, 0b00000000 );
503                 screen[ matrix*8 + line] = 0b00000000;
504                 delay_ms(500);
505         }
506
507         while (u == 0) {
508                 for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
509                         maxOne(matrix + 1, line + 1, ornament[(line+offset) % 3] );
510                 }
511                 if (offset < 2) offset ++; else offset = 0;
512                 delay_ms(1000);
513         }
514         
515         score = 1;
516         transmit_str_USART0("Score: 0000        \r");
517
518         currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)5);
519         currentTurn = 0;
520         
521 }
522
523 void stopFigure (void) {
524         uint8_t lineToPlace = 0b00000000;
525         for (uint8_t line = 0; line<4; line ++) {
526                 lineToPlace = figure[currentFigure][currentTurn][line];
527                 lineToPlace = moveLine(lineToPlace, currentX);
528                 screen[line + currentY] |= lineToPlace;
529         }
530         checkFullLine ();
531         currentY = 0;
532         currentTurn = 0;
533         currentX = STARTX;
534         currentFigure = randomDigit;
535
536         if (checkStart () != 1) redrawScreen (); else gameOver();
537 }
538
539 // --------------------------------- MAIN -----------------------------------------------------------------------
540
541 int main(void) {
542
543         // initialize the direction of PORTD to be output
544
545         set_output(DDRB, DATAIN);  
546         set_output(DDRB, LOAD);
547         set_output(DDRB, CLOCK);
548         set_output(DDRB, BEEPER);
549
550         // initiation of the max 7219
551         for (uint8_t matrix=0; matrix < INUSE; matrix++) {
552                 maxOne(matrix+1, max7219_reg_scanLimit, 0x07);      
553                 maxOne(matrix+1, max7219_reg_decodeMode, 0x00);                 // using a led matrix, not digits
554                 maxOne(matrix+1, max7219_reg_shutdown, 0x01);                   // not in shutdown mode
555                 maxOne(matrix+1, max7219_reg_displayTest, 0x00);                // no display test
556                 for (uint8_t line=0; line<8; line ++) maxOne(matrix+1, line+1, 0);      // empty registers, turn all LEDs off 
557                 maxOne(matrix+1, max7219_reg_intensity, 0x0f & 0x0f);           // the first 0x0f is the value you can set (range: 0x00 to 0x0f)
558         }
559                                                         
560         init_USART0(UBRR);  // initialize USART0
561         setRXCIE_USART0();
562         sei();
563
564         screen[ INUSE*8 ] = 0b11111111;                                         // bottom border for checking
565         currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);
566         transmit_str_USART0("Score: 0000        \r");
567
568         while (1) {
569                 if (timer % 20000 == 0) {
570                         redrawScreen ();
571                         if (checkDown() != 1) currentY ++; else {
572                                 if (prewRandomDigit == randomDigit) randomDigit++;
573                                 if (randomDigit > RAND_MAX+1) randomDigit = 0;
574                                 prewRandomDigit = randomDigit;
575                                 stopFigure ();
576                         }
577                 }
578                 timer ++;
579                 if (u != oldu){
580                         oldu = u;
581                         randomDigit = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);  
582                         //transmit_1byte_USART0(timer);
583                         //transmit_4digit_USART0(u);    
584                         if (67 == u) {
585                                 //transmit_str_USART0(" RIGHT \r");
586                                 if (checkRight () != 1) currentX ++;
587                                 redrawScreen ();
588                                 beep ();
589                         }
590                         if (68 == u) {
591                                 //transmit_str_USART0(" LEFT  \r");
592                                 if (checkLeft () != 1) currentX --;
593                                 redrawScreen ();
594                                 beep ();
595                         }
596                         if (66 == u) {
597                                 //transmit_str_USART0(" DOWN  \r");
598                                 if (checkDown() != 1) currentY ++; 
599                                 redrawScreen ();
600                                 beep ();
601                         }
602                         if (65 == u) {
603                                 //transmit_str_USART0(" UP    \r");
604                                 if (checkTurn () != 1) { 
605                                         if (currentTurn < 3) currentTurn ++; else currentTurn = 0;
606                                         redrawScreen ();
607                                         beep ();
608                                 }
609                         }
610                         u = 0;
611                 }
612         }
613
614 }
615
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717